Using View Ports
This section demonstrates how to use QuickDraw GX view ports. It shows how you can
- create and manipulate view port objects and their properties
- get and set a view port's clip and mapping
- set up a view port hierarchy attached to a window
- support scrolling in a window
- identify the view devices of a view port and the view ports of a shape
- measure a shape in the local space of a view port
Creating and Manipulating View Port Objects
QuickDraw GX provides several functions with which you can create a new view port. To create a view port that is the root view port of a hierarchy and is attached to
a Macintosh window, you use the functionGXNewWindowViewPort
, described in the Macintosh environment chapter of Inside Macintosh: QuickDraw GX Environment and Utilities. To create child view ports for that root parent, or to create a root view port for offscreen drawing, you use theGXNewViewPort
function. You can also create a new view port object by copying an existing one with theGXCopyToViewPort
function; see Listing 7-2 on page 7-44 for an example of this method.
Once you have created a view port object, you can customize its features using the techniques described in the following section.
- Replacing the default view port
- If your application draws only within windows, you may want to replace the default transform object (which references the default view port) for each shape type. You could replace it with a transform object that directly references a window view port (a view port attached to a Macintosh window). Alternatively, you could replace it with a transform that references a view port you have designated as the "current" view port; you could then redirect drawing to a window view port by assigning the window view port as the parent of the current view port. Or you could take a different approach and explicitly assign a child view port of a window view port to each shape as it is created, using the
GXSetShapeViewPorts
function.![]()
You can test if two view port-object references refer to the same view port object by simply testing the references for equality. You can also test a view port for equality with another view port with the
GXEqualViewPort
function. For two view port objects to be equal, their mappings, clips, dithers, halftones, attributes, parent view ports, and view groups must be identical; if one view port is attached to a window, the other view port must be attached to the same window. The tag lists or child view ports of the view ports need not be identical. View port object copies created with theGXCopyToViewPort
function are always equal to the view port from which they were copied.To delete your application's reference to a view port object, call the
GXDisposeViewPort
function. Because view port objects have no owner count,
callingGXDisposeViewPort
actually releases the memory allocated for that view port object, and invalidates all other references to it. Therefore, once you have disposed
of a view port, other transform objects that reference that view port will have invalid view port references in their view port lists. This causes no error when you try to draw; drawing simply does not occur to view ports whose references are invalid.The following code fragment first creates a Macintosh window (
sampleWindow
), and then usesGXNewWindowViewPort
to create a view port attached to it. When it no longer needs them, the code disposes of the view port and then the window. TheNewWindow
function and its parameters, and theDisposeWindow
function, are described in the Window Manager chapter of Inside Macintosh: Macintosh Toolbox Essentials.sampleWindow = NewWindow( nil, &windowRect, "\p", true, documentProc, (WindowPtr)-1L, true, 0L ); aViewPort = GXNewWindowViewPort(sampleWindow); . . /* use the window and view port */ . GXDisposeViewPort(aViewPort); DisposeWindow(sampleWindow);The following line of code creates a view port that is not attached to a window. You might use this call to create a view port that is to be the child of another view port. The code assigns the new view port to the gxScreenViewDevices view group, the view group for all onscreen drawing:
myChildViewPort = GXNewViewPort(gxScreenViewDevices);A more general way to assign the view group parameter is to first call theGXGetViewPortViewGroup
function to determine the view group of the intended parent view port, and use that result as the parameter for GXNewViewPort. See, for example, Listing 7-5 on page 7-47.The
GXNewViewPort
function is described on page 7-70. TheGXCopyToViewPort
function is described on page 7-72. TheGXEqualViewPort
function is described on page 7-73. TheGXDisposeViewPort
function is described on page 7-71.Manipulating View Port Object Properties
This section describes how to manipulate the dither, halftone, view group, attributes, and tag list properties of a view port object:
How to manipulate other view port properties is described in subsequent sections, starting with "Getting and Setting a View Port's Clip and Mapping" on page 7-44.
- To manipulate the dither level, you use the functions GXGetViewPortDither and GXSetViewPortDither.
- To manipulate the halftone structure, you use the functions GXGetViewPortHalftone and GXSetViewPortHalftone.
- To manipulate the view group reference, you use the functions GXGetViewPortViewGroup and GXSetViewPortViewGroup.
- To manipulate the view port attributes, you use the functions GXGetViewPortAttributes and GXSetViewPortAttributes.
- To manipulate the view port tag list, you use the functions
GXGetViewPortTags and GXSetViewPortTags.
Getting and Setting a View Port's Dither, Halftone, and Attributes
Listing 7-1 is an example of code that sets the dither level, halftone structure, and view port attributes of the view port myViewPort. For the halftone structure (myHalfTone), the code sets all of its values, including the background color and the dot color in HSV color space. The tint type selected, however, is luminance tint, meaning that only the lightness of the input color is used to calculate the proportion of dot and background to use for the halftone. The attributes specify a grayscale view port, meaning that the dot and background colors are also drawn in gray.Listing 7-1 Changing a view port's dither, halftone, and attributes
gxViewPort myViewPort gxHalftone myHalfTone; GXSetViewPortAttributes(myViewPort, gxGrayPort); GXSetViewPortDither (myViewPort, 4); myHalfTone.angle = ff(6); myHalfTone.frequency = ff(24); myHalfTone.method = gxDispersedDot; myHalfTone.tinting = gxLuminanceTint; myHalfTone.tintSpace = gxHSVSpace; myHalfTone.backgroundColor.space = gxHSVSpace; myHalfTone.backgroundColor.profile = nil; myHalfTone.backgroundColor.element.hsv.value = 0xFFFF; myHalfTone.backgroundColor.element.hsv.saturation = 0xCCCD; myHalfTone.backgroundColor.element.hsv.hue = 0x8000; myHalfTone.dotColor.space = gxHSVSpace; myHalfTone.dotColor.profile = nil; myHalfTone.dotColor.element.hsv.value = 0xFFFF; myHalfTone.dotColor.element.hsv.saturation = 0xAD1C; myHalfTone.dotColor.element.hsv.hue = 0xE4F9; GXSetViewPortHalftone(myViewPort, &myHalfTone);The
- Note
- Dithers and halftones are mutually exclusive. The halftone in this example overrides the dither, so dithering is not performed at drawing.
![]()
GXGetViewPortDither
function is described on page 7-80; theGXSetViewPortDither
function is described on page 7-80.The
GXGetViewPortHalftone
function is described on page 7-81; theGXSetViewPortHalftone
function is described on page 7-82.The
GXGetViewPortAttributes
function is described on page 7-89; theGXSetViewPortAttributes
function is described on page 7-90.Getting and Setting a View Port's View Group
Listing 7-2 demonstrates the use of theGXSetViewPortViewGroup
function. It is part of a routine that creates an offscreen view group (newGroup
) that is a copy of an existing view group (group
). For each view port in the onscreen view group, the routine creates a copy. It then usesGXSetViewPortViewGroup
to assign the proper view group to the new view port. (Listing 7-10 on page 7-54 shows another part of the same routine.)The routine uses the
count
variable to decrement through the list of view ports (oldList
, retrieved through two consecutive calls toGXGetViewGroupViewPorts
) belonging to the onscreen view group. The code simultaneously builds, for its own purposes, a list (newList
) of view ports for the offscreen view group, usingGXCopyToViewPort
andGXSetViewPortViewGroup
to copy each view port into the offscreen view group and set its view group property.Listing 7-2 Copying the view ports from one view group to another
long portCount = GXGetViewGroupViewPorts(group, nil); long count = portCount; gxViewPort *oldPortList = (void *)NewPtr(portCount * sizeof(gxViewPort)); gxViewPort *oldList = oldPortList; gxViewPort *newPortList = (void *)NewPtr(portCount * sizeof(gxViewPort)); gxViewPort *newList = newPortList; GXGetViewGroupViewPorts(group, oldPortList); while (count-- > 0) GXSetViewPortViewGroup(*newList++ = GXCopyToViewPort(nil, *oldList++), newGroup);TheGXGetViewPortViewGroup
function is described on page 7-88. TheGXSetViewPortViewGroup
function is described on page 7-88.Getting and Setting a View Port's Tag References
You can examine the list of references to tag objects currently associated with a view port object using theGXGetViewPortTags
function. Once you create a tag object, you can attach it to a view port object using theGXSetViewPortTags
function. You can attach as many tag objects as you like to a view port object.Tag objects and the basic functions for manipulating them are described in the chapter "Tag Objects" in this book. That chapter also lists the common tag types defined and reserved by Apple Computer, Inc.
The
GXGetViewPortTags
function is described on page 7-91. TheGXSetViewPortTags
function is described on page 7-92.Getting and Setting a View Port's Clip and Mapping
The clip and mapping properties of a view port control the visibility and location of its contents. For onscreen view ports attached to Macintosh windows, you do not directly set the clip or mapping properties; you move or resize the window with Window Manager calls, and QuickDraw GX automatically updates the view port's clip and mapping. For child view ports of window view ports, however, and for all offscreen view ports, you must set the clip and mapping yourself.You use the functions
GXGetViewPortMapping
, andGXSetViewPortMapping
to set a view port mapping to move the contents of the view port, such as when scrolling. You also set the view port mapping to provide scaled, rotated, or otherwise altered views of the view port's contents. Listing 7-3 shows an example that uses those functions plusGXGetViewPortClip
andGXScaleMapping
to scale the view port myViewPort
to 200 percent of its original size, about an origin at the center of the view port's clip.Listing 7-3 Changing a view port's mapping
gxViewPort myViewPort gxMapping myViewPortMapping; gxShape myViewPortFrame; gxPoint center; GXGetViewPortMapping(myViewPort, &myViewPortMapping); myViewPortFrame = GXGetViewPortClip(myViewPort); GXGetShapeCenter(myViewPortFrame, 0L, ¢er); GXScaleMapping(&myViewPortMapping, ff(2), ff(2), center.x, center.y); GXSetViewPortMapping(myViewPort, &myViewPortMapping); GXDisposeShape(myViewPortFrame);Note that, because the GXGetViewPortClip function creates a shape object, the code in Listing 7-3 disposes of the shape after using it. TheGXGetShapeBounds
function is described in the geometric operations chapter of Inside Macintosh: QuickDraw GX Graphics; theGXSetShapeMapping
function is described in the mathematics chapter of Inside Macintosh: QuickDraw GX Environment and Utilities.
You can use the
- Getting the global mapping
- If a view port is a child view port in a hierarchy, its mapping converts from local space into the local space of its parent view port, not directly into global space. If you want to determine the resultant mapping obtained by concatenating the mappings of a view port and all its parents--a mapping from local space all the way into global space--you can use GXGetViewPortGlobalMapping, which is described on page 7-79. For an example of its use, see Listing 7-11 on page 7-57.
![]()
GXSetViewPortClip
function to set a view port clip to initialize or change the visible area of the view port. Listing 7-4 is a routine that sets up the clip of a child view port (gcontentViewPort) whose parent is the root view port attached to a Macintosh window (theWindow). The routine makes the clip the same size as the window's content area, minus the width of the scroll bars on the window's side and bottom.The listing uses the application-defined function GetWindowBoundsShape to determine the rectangle shape corresponding to the content area of the window. That function retrieves a QuickDraw rectangle corresponding to the port rectangle of the window, and then converts it to a QuickDraw GX rectangle using the GXConvertQDPoint function.
Listing 7-4 Setting a view port clip
void ResetContentViewPortClip (WindowPtr theWindow) { gxRectangle viewRect; gxShape contentViewPortClipShape; /* get the size of the window port rect */ GetWindowBoundsShape(theWindow, &viewRect); /* Adjust the rectangle to accommodate the scroll bars */ viewRect.right -= ff(kScrollBarWidth - 1); viewRect.bottom -= ff(kScrollBarWidth - 1); /* assign it as the clip shape */ contentViewPortClipShape = GXNewRectangle(&viewRect); GXSetViewPortClip(gcontentViewPort, contentViewPortClipShape); GXDisposeShape (contentViewPortClipShape); }TheGXConvertQDPoint
function is described in the Macintosh environment chapter
of Inside Macintosh: QuickDraw GX Environment and Utilities. TheGXNewRectangle
function, which creates a rectangle shape, is described in the geometric shapes chapter of Inside Macintosh: QuickDraw GX Graphics.The
GXGetViewPortClip
function is described on page 7-74; theGXSetViewPortClip
function is described on page 7-75.
TheGXGetViewPortMapping
function is described on page 7-77; theGXSetViewPortMapping
function is described on page 7-78.Setting Up the View Port Hierarchy for a Window
Setting up a view port hierarchy means assigning the appropriate parent view port and child view port references to all view ports involved. The functions you use areGXGetViewPortParent
,GXSetViewPortParent
,GXGetViewPortChildren
, andGXSetViewPortChildren
. Take these steps to set up a simple hierarchy in which a child view port is used for drawing a window's content:
Note that you do not have to add the child view port to the window view port's
- Create the child view port in the window view port's view group.
- Create a clip shape and assign it to the child view port. Set the child view port's mapping.
- Assign the window view port as the parent of the child view port.
- Dispose of the clip shape.
list of children; when you set the parent view port property of the child view port, QuickDraw GX adds the child view port to the parent's list of child view ports.Listing 7-5 is an example that sets up such a hierarchy. It creates a view port with the
GXNewViewPort
function, and usesGXGetViewPortViewGroup
to find out what view group to assign it to. The code assigns properties to the view port withGXSetViewPortClip
,GXSetViewPortMapping
, andGXSetViewPortParent
. The window view port iswindowParentViewPort
, and the rectangleviewRect
defines a clipping area that is the size of the window minus the area reserved for scroll bars.Listing 7-5 Setting up a view port for a window
gxRectangle viewRect; gxViewPort windowParentViewPort; gxShape contentViewPortShape; . /* . Create window view port with GXNewWindowViewPort. Make . viewRect equal to port rectangle minus scroll bars. */ gcontentViewPort = GXNewViewPort (GXGetViewPortViewGroup(windowParentViewPort)); contentViewPortShape = GXNewRectangle(&viewRect); GXSetViewPortClip(gcontentViewPort, contentViewPortShape); GXSetViewPortMapping(gcontentViewPort, nil); GXSetViewPortParent(gcontentViewPort, windowParentViewPort); GXDisposeShape (contentViewPortShape);Once you have set up a hierarchy, if you want to draw into the child view port--and thus onscreen--you must place a reference to the child view port in a transform object's view port list, and the shapes you draw must reference that transform.The
GXGetViewPortParent
function is described on page 7-84; theGXSetViewPortParent
function is described on page 7-84.
TheGXGetViewPortChildren
function is described on page 7-86; theGXSetViewPortChildren
function is described on page 7-87.Supporting Scrolling in a Window
To support scrolling in a view port attached to a Macintosh window, you need to create a child view port of the window view port, and draw into it rather than into its parent. QuickDraw GX prevents you from changing the mapping or clip of a view port directly attached to a Macintosh window.When the user scrolls the window, you manipulate the child view port's mapping to scroll the content. When the user resizes the window, you manipulate the child view port's clip to fit the new window shape. When the user moves the window, you do nothing; QuickDraw GX takes care of positioning both parent and child view ports.
Listing 7-6 is an example of a scrolling routine that scrolls the contents of a child view port (
gcontentViewPort
) by specified vertical and horizontal amounts (hScroll
andvScroll
), in response to mouse-down events in the scroll bars of a window (theWindow
). The event-dispatching routine calls this scrolling routine after it has calculated how much scrolling is required. After the scrolling routine executes, a separate routine (not shown) updates the appearance of the scroll bars.Listing 7-6 Supporting scrolling in a child view port
void DoScroll(WindowPtr theWindow, short hScroll, short vScroll) { Rect scrollRect; Point scrollPt; RgnHandle myRgn; gxMapping viewPortMapping; if ((hScroll == 0) && (vScroll == 0)) return; /* Get the child view port's mapping, adjust it for the horizontal and vertical scroll, then reassign it to the view port. The next drawing action will then reflect the scrolled positions of shapes in the view port. */ GXGetViewPortMapping(gcontentViewPort, &viewPortMapping); MoveMapping(&viewPortMapping, ff(hScroll), ff(vScroll)); GXSetViewPortMapping(gcontentViewPort, &viewPortMapping); /* Shift the pixels representing the already drawn contents of window, so that less will have to be drawn when the window is updated. Only the parts scrolled into view (specified by the update region myRgn) will need to be redrawn. */ scrollRect = theWindow->portRect; scrollRect.right -= (kScrollBarWidth-1); scrollRect.bottom -= (kScrollBarWidth-1); SetPort(theWindow); myRgn = NewRgn(); ScrollRect(&scrollRect, hScroll, vScroll, myRgn); /* update origin position in app's extended window record */ SetPt(&scrollPt, hScroll, vScroll); AddPt(scrollPt, &((MyWindowPeek)theWindow)->origin); /* redraw the window and dispose of the region handle */ DrawWindow(theWindow); DisposeRgn(myRgn); }Identifying a View Port's View Devices
TheGXGetViewPortViewDevices
function returns a list of all view devices that could be affected by shapes drawn in a given view port. Within the view group of the view port, all view device objects whose clip areas overlap the clip area of the view port appear in the list returned by this function.You can use
GXGetViewPortViewDevices
to determine whether a given view device can be affected by drawing into a given view port. You can also use it to examine the properties of all devices that you might draw to, perhaps in order to assign appropriate properties to offscreen view devices.Listing 7-7 is a library function (
SetShapeFastXorTransfer
) that uses another library function (SetInkFastXorTransfer
) to set up a shape's color and an XOR transfer mode so that drawing with that color will cause a specified highlight color to replace the background color in the destination. TheSetInkFastXorTransfer
function is not shown here. Listing 7-7 is shown because it usesGXGetViewPortViewDevices
to get a list of the view devices a view port can draw to, although it actually uses only the first view device in the list.Listing 7-7 Setting a shape color for XOR highlighting
void SetShapeFastXorTransfer(gxShape source, gxColor *background, gxColor *result) { long viewPortCount, viewDeviceCount; void *buffer; gxViewPort vp; gxViewDevice vd; gxInk inky; /* get size of view port list, then allocate buffer for it */ viewPortCount = GXGetTransformViewPorts( GXGetShapeTransform(source), nil); buffer = NewPtr(sizeof(gxViewPort) * viewPortCount); /* check for memory error (not shown), then get list itself */ GXGetTransformViewPorts(GXGetShapeTransform(source), (gxViewPort *)buffer); /* get no. of view devices, then allocate buffer for list */ viewDeviceCount = GXGetViewPortViewDevices( vp = *(gxViewPort *)buffer, nil); . . /* check for memory error (not shown), then get list itself */ . GXGetViewPortViewDevices(vp, (gxViewDevice *)buffer); vd = *(gxViewDevice *)buffer; DisposePtr(buffer); /* get shape's ink; if shared, assign a copy of the ink */ if (GXGetInkOwners(inky = GXGetShapeInk(source)) > 1) { GXSetShapeInk(source, inky = GXNewInk()); GXDisposeInk(inky); } /* set ink's transfer mode and suppress dithering */ SetInkFastXorTransfer(inky, vd, vp, background, result); GXSetShapeInkAttributes(source, GXGetShapeInkAttributes(source) | gxSuppressDitherInk); }TheGXGetViewPortViewDevices
function is described on page 7-94.Identifying a Shape's View Ports
TheGXGetShapeGlobalViewPorts
function returns a list of all view ports that a shape could actually appear in if it were drawn. If a shape's transform references a view port, and if that view port's clip does not totally exclude the shape from the visible part of the view port, the view port appears in the list returned by this function.You can use
GXGetShapeGlobalViewPorts
to avoid the overhead of drawing shapes that cannot be visible. You can also use it as an input to theGXGetShapeGlobalViewDevices
function to determine all the devices on which a given shape can appear.The
GXGetShapeGlobalViewPorts
function is described on page 7-95. TheGXGetShapeGlobalViewDevices
function is described on page 7-115.Measuring a Shape in Local Space
TheGXGetShapeLocalBounds
function measures the bounding rectangle of a shape in local coordinates--that is, after the transform mapping has been applied to the shape geometry. You can useGXGetShapeLocalBounds
to compare the positions and sizes of two shapes in the same view port, even if they do not share the same transform
object. (To compare the positions and sizes of two shapes in different view ports, use
theGXGetShapeGlobalBounds
function; to measure a shape on a view device, useGXGetShapeDeviceBounds
.)Listing 7-8 is a function in a shape-editing program. It draws a gray box representing
the bounding rectangle for each shape in a list of shapes passed to it. It callsGXGetShapeLocalBounds
for each shape, and then defines and draws a rectangle shape whose geometry matches that bounding rectangle. Regardless of how each shape has been modified by its own transform mapping,GXGetShapeLocalBounds
returns a rectangle (whose default transform has an identity mapping) that exactly matches the transformed shape's bounding rectangle when drawn in the view port.Listing 7-8 makes use of the library function
SetShapeCommonColor
to set the bounding rectangle's color.Listing 7-8 Locating the bounding rectangles of a list of shapes in a view port
void ShowLocalBounds(gxShape *p1stShape, long shapeCount) { register gxShape *pShape, rectShape; gxRectangle bounds; pShape = p1stShape + shapeCount - 1; /* no. of last shape */ /* define a framed gray rectangle shape for the bounds */ rectShape = GXNewShape(gxRectangleType); GXSetShapeFill(rectShape, gxClosedFrameFill); SetShapeCommonColor(rectShape, gxGray); /* go through shape list, get and draw local bounds for each */ while (shapeCount--) { GXGetShapeLocalBounds(*pShape--, &bounds); GXSetRectangle(rectShape, &bounds); GXDrawShape(rectShape); } GXDisposeShape(rectShape); }TheGXGetShapeLocalBounds
function is described on page 7-96.The
GXGetShapeGlobalBounds
function is described on page 7-125; theGXGetShapeDeviceBounds
function is described on page 7-116.